﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Configuration;
using Microsoft.FxCop.Sdk;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics;


/// <summary>
/// http://www.java2s.com/Open-Source/CSharp/Web-Service/Web%20Service%20Software%20Factory/Microsoft/Practices/FxCop/Rules/WcfSemantic/ConfigurationIntrospectionRule.cs.htm
/// </summary>
public abstract class ConfigurationIntrospectionRule : BaseRule
{
    private string sourceFile;
    protected bool _ConfigProbingStarted { get; private set; }
    protected bool _ConfigProbingStopped { get; private set; }

    private const string ConfigFileSearchPattern = "*.config";
    /// <summary>
    /// Initializes a new instance of the <see cref="T:ConfigurationIntrospectionRule"/> class.
    /// </summary>
    /// <param name="name">The name.</param>
    protected ConfigurationIntrospectionRule(string name) : base(name) { }
    /// <summary>
    /// Gets the source file.
    /// </summary>
    /// <value>The source file.</value>
    public string SourceFile
    {
        get { return sourceFile; }
        set
        {
            if (value == null)
                throw new ArgumentNullException("value");
            // update the sourceFile in case it's not in the system a temp folder.
            if (!value.StartsWith(Path.GetTempPath(), StringComparison.InvariantCultureIgnoreCase))
            {
                sourceFile = value;
            }
        }
    }

    /// <summary>
    /// Checks the specified configuration.
    /// </summary>
    /// <param name="configuration">The configuration.</param>
    /// <returns></returns>
    public abstract Microsoft.FxCop.Sdk.ProblemCollection Check(ModuleNode module,Configuration configuration);
    
    /// <summary/>
    /// // this override will get a config file (if any) from the same location as the current inspected asm.
    public override ProblemCollection Check(ModuleNode module)
    {
        Debug.Assert(_ConfigProbingStarted == false);
        _ConfigProbingStarted = true;
        Configuration configuration;
        try
        {
            configuration = ConfigurationProbing(module);
        }
        finally
        {
            _ConfigProbingStopped = true;
        }
        if (configuration != null)
        {
            this.SourceFile = configuration.FilePath;
            
            return Check(module,configuration);
        }

       

        return Problems;
    }
    // FxCop: False positive
    [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
    private Configuration ConfigurationProbing(ModuleNode module)
    {


        if (module == null ||
            string.IsNullOrEmpty(module.Directory))
        {
            return null;
        }
        
        try
        {
            string[] configs = Directory.GetFiles(module.Directory, ConfigFileSearchPattern, SearchOption.TopDirectoryOnly);
            if (configs.Length == 0 &&
                IsAspGeneratedAssembly(module))
            {

                // try the parent folder (this scenario is for web apps, asms in Bin folder and config in parent folder)
                configs = Directory.GetFiles(Path.GetDirectoryName(module.Directory), ConfigFileSearchPattern, SearchOption.TopDirectoryOnly);
            }
            string configPath = SelectConfigFile(configs, module.Location);
            if (string.IsNullOrEmpty(configPath))
            {
                return null;
            }
            ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
            fileMap.ExeConfigFilename = configPath;
            return ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
        }
        catch (Exception e)
        {
            TraceManager.Exception(e);
            return null;
        }
    }

    private string SelectConfigFile(string[] configs, string moduleLocation)
    {
        string selected = null;

        if (configs.Length != 0)
        {
            List<string> lookup = new List<string>(configs);
            // probe for App config file (config name = exe name + [.config] extension) 
            selected = moduleLocation + ".config";
            if (!lookup.Contains(selected))
            {
                // probe for web config file
                selected = lookup.Find(delegate(string configPath)
                {
                    return Path.GetFileName(configPath).Equals("web.config", StringComparison.InvariantCultureIgnoreCase);
                });
            }
        }
        return selected;
    }

    /// <summary>
    /// http://social.msdn.microsoft.com/forums/en-US/vstscode/thread/38d77d18-9715-402e-9a86-a6a727bcf0d7/
    /// </summary>
    /// <param name="module"></param>
    /// <returns></returns>
    static bool IsAspGeneratedAssembly(ModuleNode module)
    {
        if (module == null) throw new ArgumentNullException("module");
        if (module.NodeType != NodeType.Assembly) return false;
        for (int i = 0; i < module.Attributes.Count; i++)
        {
            AttributeNode a = module.Attributes[i];
            if (a.Type.Name.Name == "AspNetGeneratedCodeAttribute")
                return true;

            if (a.Type.Name.Name == "GeneratedCodeAttribute"
              && a.Expressions.Count > 0)
            {
                Literal literal = a.Expressions[0] as Literal;
                if (literal != null)
                {
                    string tool = literal.Value as string;
                    if (tool != null
                      && 0 == String.Compare("ASP.NET", tool, StringComparison.OrdinalIgnoreCase))
                        return true;
                }
            }
        }
        return false;
    }

}

